home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / hobby / ast44src.zip / IO.C < prev    next >
C/C++ Source or Header  |  1995-02-11  |  23KB  |  698 lines

  1. /*
  2. ** Astrolog (Version 4.40) File: io.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (astara@u.washington.edu). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 1/29/1995.
  35. */
  36.  
  37. #include "astrolog.h"
  38.  
  39.  
  40. /*
  41. ******************************************************************************
  42. ** File IO Routines.
  43. ******************************************************************************
  44. */
  45.  
  46. /* Open the file indicated by the given string and return the file's stream */
  47. /* pointer, or NULL if the file couldn't be found or opened. All parts of   */
  48. /* the program which open files to read call this routine. We look in       */
  49. /* several various locations and directories for the file before giving up. */
  50.  
  51. FILE *FileOpen(szFile, nFileMode)
  52. char *szFile;
  53. int nFileMode;
  54. {
  55.   FILE *file;
  56.   char name[cchSzDef], mode[3];
  57. #ifdef ENVIRON
  58.   char *env;
  59. #endif
  60.  
  61.   /* Some file types we want to open as binary instead of Ascii. */
  62.   sprintf(mode, "r%s", nFileMode == 2 ? "b" : "");
  63.  
  64.   /* First look for the file in the current directory. */
  65.   file = fopen(szFile, mode);
  66.   if (file != NULL)
  67.     return file;
  68.  
  69. #ifdef ENVIRON
  70.   /* Next look for the file in the directory indicated by the version */
  71.   /* specific system environment variable.                            */
  72.   sprintf(name, "%s%s", ENVIRONVER, szVersionCore);
  73.   env = getenv(name);
  74.   if (env && *env) {
  75.     sprintf(name, "%s%c%s", env, chDirSep, szFile);
  76.     file = fopen(name, mode);
  77.     if (file != NULL)
  78.       return file;
  79.   }
  80.  
  81.   /* Next look in the directory in the general environment variable. */
  82.   env = getenv(ENVIRONALL);
  83.   if (env && *env) {
  84.     sprintf(name, "%s%c%s", env, chDirSep, szFile);
  85.     file = fopen(name, mode);
  86.     if (file != NULL)
  87.       return file;
  88.   }
  89.  
  90.   /* Next look in the directory in the version prefix environment variable. */
  91.   env = getenv(ENVIRONVER);
  92.   if (env && *env) {
  93.     sprintf(name, "%s%c%s", env, chDirSep, szFile);
  94.     file = fopen(name, mode);
  95.     if (file != NULL)
  96.       return file;
  97.   }
  98. #endif
  99.  
  100.   /* Finally look in one of several directories specified at compile time. */
  101.   sprintf(name, "%s%c%s", nFileMode == 0 ? DEFAULT_DIR :
  102.     (nFileMode == 1 ? CHART_DIR : EPHE_DIR), chDirSep, szFile);
  103.   file = fopen(name, mode);
  104.   if (file == NULL && nFileMode == 1) {
  105.     /* If the file was never found, print an error (unless we were looking */
  106.     /* for a certain file type, e.g. the optional astrolog.dat file).      */
  107.     sprintf(name, "File '%s' not found.", szFile);
  108.     PrintError(name);
  109.   }
  110.   return file;
  111. }
  112.  
  113.  
  114. /* This is Astrolog's generic file processing routine, which handles chart */
  115. /* info files, position files, and config files. Given a file name or a    */
  116. /* file handle, run through each line as a series of command switches.     */
  117.  
  118. bool FProcessSwitchFile(szFile, file)
  119. char *szFile;
  120. FILE *file;
  121. {
  122.   char szLine[cchSzMax], *argv[MAXSWITCHES], ch;
  123.   int argc, i;
  124.  
  125.   if (file == NULL)
  126.     file = FileOpen(szFile, 0);
  127.   if (file == NULL)
  128.     return fFalse;
  129.  
  130.   /* All files have to begin with the -@ switch file type identifier. */
  131.   ch = getc(file); ungetc(ch, file);
  132.   if (ch != '@') {
  133.     sprintf(szLine, "The command file '%s' is not in any valid format.",
  134.       szFile);
  135.     PrintWarning(szLine);
  136.     return fFalse;
  137.   }
  138.  
  139.   while (!feof(file)) {
  140.     while (!feof(file) && (ch = getc(file)) < ' ')
  141.       ;
  142.     for (szLine[0] = ch, i = 1; i < cchSzMax && !feof(file) &&
  143.       (szLine[i] = getc(file)) >= ' '; i++)
  144.       ;
  145.     szLine[i] = chNull;
  146.     argc = NParseCommandLine(szLine, argv);
  147.     if (!FProcessSwitches(argc, argv))
  148.       return fFalse;
  149.   }
  150.   return fTrue;
  151. }
  152.  
  153.  
  154. /* Take the current chart information, and write it out to the file   */
  155. /* as indicated by the -o switch. This is only executed at the end of */
  156. /* program execution if the -o switch is in effect.                   */
  157.  
  158. bool FOutputData()
  159. {
  160.   char sz[cchSzDef];
  161.   FILE *file;
  162.   int i, j;
  163.   real rT;
  164.  
  165.   file = fopen(is.szFileOut, "w");  /* Create and open the file for output. */
  166.   if (file == NULL) {
  167.     sprintf(sz, "File %s can not be created.", is.szFileOut);
  168.     PrintError(sz);
  169.     return fFalse;
  170.   }
  171.   if (!us.fWritePos) {
  172.  
  173.     /* Write the chart information to the file. */
  174.  
  175.     if (Mon < 1) {
  176.       fclose(file);
  177.       PrintError("Can't output chart with no time/space to file.");
  178.       return fFalse;
  179.     }
  180.     if (us.fWriteOld) {
  181.       fprintf(file, "%d\n%d\n%d\n%.2f\n%.2f\n%.2f\n%.2f\n",
  182.         Mon, Day, Yea, Tim, Zon-Dst, Lon, Lat);
  183.     } else {
  184.       fprintf(file, "@0102  ; %s chart info.\n", szAppName);
  185.       i = us.fAnsi;
  186.       us.fAnsi = fFalse;
  187.       fprintf(file, "%cqb %c%c%c %d %d %s %s %s %s\n", chSwitch, chMon3(Mon),
  188.         Day, Yea, SzTim(Tim), Dst == 0.0 ? "ST" : (Dst == 1.0 ? "DT" :
  189.         SzZone(Dst)), SzZone(-Zon), SzLocation(Lon, Lat));
  190.       fprintf(file, "%czi \"%s\" \"%s\"\n", chSwitch, ciMain.nam, ciMain.loc);
  191.       us.fAnsi = i;
  192.     }
  193.   } else {
  194.  
  195.     /* However, if the -o0 switch is in effect, then write the actual */
  196.     /* positions of the planets and houses to the file instead.       */
  197.  
  198.     if (us.fWriteOld) {
  199.       for (i = 1; i <= oNorm; i++) {
  200.         j = (int)planet[i];
  201.         fprintf(file, "%c%c%c: %2d %2d %10.7f\n", chObj3(i),
  202.           j%30, j/30+1, RFract(planet[i])*60.0);              /* Position */
  203.         rT = planetalt[i];
  204.         fprintf(file, "[%c]: %3d %12.8f\n",                   /* Altitude */
  205.           ret[i] >= 0.0 ? 'D' : chRet, (int)(RSgn(rT)*
  206.           RFloor(RAbs(rT))), (rT-(real)(int)rT)*60.0);     /* Retrograde? */
  207.         if (i == oNod)
  208.           i = oFor-1;
  209.         else if (i == oFor)
  210.           i = oMC -1;
  211.         else if (i == oMC)
  212.           i = oAsc-1;
  213.         else if (i == oAsc)
  214.           i = oVtx-1;
  215.         else if (i == oVtx)    /* Skip minor cusps to write uranians  */
  216.           i = us.fUranian ? uranLo-1 : cObj;
  217.       }
  218.       for (i = 1; i <= cSign/2; i++) {   /* Write first six cusp positions */ 
  219.         j = (int)house[i];
  220.         fprintf(file, "H_%c: %2d %2d %10.7f\n",
  221.           'a'+i-1, j%30, j/30+1, RFract(house[i])*60.0);
  222.       }
  223.  
  224.     } else {
  225.       fprintf(file, "@0203  ; %s chart positions.\n", szAppName);
  226.       for (i = 1; i <= cObj; i++) if (!ignore[i] || FCusp(i)) {
  227.         fprintf(file, "%cYF ", chSwitch);
  228.         if (i <= oNorm)
  229.           fprintf(file, "%c%c%c", chObj3(i));
  230.         else
  231.           fprintf(file, "%3d", i);
  232.         rT = FBetween(i, cuspLo-1+4, cuspLo-1+9) ?
  233.           house[i-(cuspLo-1)] : planet[i];
  234.         j = (int)rT;
  235.         fprintf(file, ":%3d %c%c%c%13.9f,%4d%13.9f,",
  236.           j%30, chSig3(j/30+1), RFract(rT)*60.0,
  237.           (int)planetalt[i], RFract(RAbs(planetalt[i]))*60.0);
  238.         rT = i > oNorm ? 999.0 : (i == oMoo && !us.fPlacalc ? 0.0026 :
  239.           RSqr(spacex[i]*spacex[i]+spacey[i]*spacey[i]+spacez[i]*spacez[i]));
  240.         fprintf(file, "%14.9f%14.9f\n", DFromR(ret[i]), rT);
  241.       }
  242.     }
  243.   }
  244.  
  245.   /* Now write any extra strings that were on the command line after the -o */
  246.   /* specification but before the next switch, to the file as comments.     */
  247.  
  248.   for (i = 1; i < is.cszComment; i++) {
  249.     is.rgszComment++;
  250.     fprintf(file, "%s%s\n", us.fWriteOld ? "" : "; ", is.rgszComment[1]);
  251.   }
  252.   fclose(file);
  253.   return fTrue;
  254. }
  255.  
  256.  
  257. /*
  258. ******************************************************************************
  259. ** User Input Routines.
  260. ******************************************************************************
  261. */
  262.  
  263. /* Given a string, return an index number corresponding to what the string */
  264. /* indicates, based on a given parsing mode. In most cases this is mainly  */
  265. /* looking up a string in the appropriate array and returning the index.   */
  266.  
  267. int NParseSz(szEntry, pm)
  268. char *szEntry;
  269. int pm;
  270. {
  271.   char szLocal[cchSzMax], *sz, ch0, ch1, ch2;
  272.   int cch, n, i;
  273.  
  274.   /* First strip off any leading or trailing spaces. */
  275.   for (cch = 0; szLocal[cch] = szEntry[cch]; cch++)
  276.     ;
  277.   while (cch && szLocal[cch-1] <= ' ')
  278.     szLocal[--cch] = chNull;
  279.   for (sz = szLocal; *sz && *sz <= ' '; sz++, cch--)
  280.     ;
  281.  
  282.   if (cch >= 3) {
  283.     ch0 = ChCap(sz[0]); ch1 = ChUncap(sz[1]); ch2 = ChUncap(sz[2]);
  284.     switch (pm) {
  285.     /* Parse months, e.g. "February" or "Feb" -> 2 for February. */
  286.     case pmMon:
  287.       for (i = 1; i <= cSign; i++) {
  288.         if (ch0 == szMonth[i][0] && ch1 == szMonth[i][1] &&
  289.           ch2 == szMonth[i][2])
  290.           return i;
  291.       }
  292.       break;
  293.     /* Parse planets, e.g. "Jupiter" or "Jup" -> 6 for Jupiter. */
  294.     case pmObject:
  295.       for (i = 1; i <= cObj; i++) {
  296.         if (ch0 == szObjName[i][0] && ch1 == szObjName[i][1] &&
  297.           ch2 == szObjName[i][2])
  298.           return i;
  299.       }
  300.       if (ch0 == 'L' && ch1 == 'i' && ch2 == 'l')
  301.         return oLil;
  302.       if (ch0 == 'S' && ch1 == '.' && ch2 == 'n')
  303.         return oSou;
  304.       break;
  305.     /* Parse aspects, e.g. "Conjunct" or "Con" -> 1 for the Conjunction. */
  306.     case pmAspect:
  307.       for (i = 1; i <= cAspect; i++) {
  308.         if (ch0 == szAspectAbbrev[i][0] &&
  309.           ch1 == ChUncap(szAspectAbbrev[i][1]) &&
  310.           ch2 == szAspectAbbrev[i][2])
  311.           return i;
  312.       }
  313.       break;
  314.     /* Parse house systems, e.g. "Koch" or "Koc" -> 1 for Koch houses. */
  315.     case pmSystem:
  316.       for (i = 1; i <= cSystem; i++) {
  317.         if (ch0 == szSystem[i][0] && ch1 == szSystem[i][1] &&
  318.           ch2 == szSystem[i][2])
  319.           return i;
  320.       }
  321.     /* Parse zodiac signs, e.g. "Scorpio" or "Sco" -> 8 for Scorpio. */
  322.     case pmSign:
  323.       for (i = 1; i <= cSign; i++) {
  324.         if (ch0 == szSignName[i][0] && ch1 == szSignName[i][1] &&
  325.           ch2 == szSignName[i][2])
  326.           return i;
  327.       }
  328.     /* Parse colors, e.g. "White" or "Whi" -> 15 for White. */
  329.     case pmColor:
  330.       for (i = 0; i < 16 ; i++) {
  331.         if (ch0 == szColor[i][0] && ch1 == szColor[i][1] &&
  332.           ch2 == ChUncap(szColor[i][2]))
  333.           return i;
  334.       }
  335.     }
  336.   }
  337.   n = atoi(sz);
  338.  
  339.   if (pm == pmYea) {
  340.     /* For years, process any "BC" (or "B.C.", "b.c", and variations) and   */
  341.     /* convert an example such as "5BC" to -4. For negative years, note the */
  342.     /* difference of one, as 1AD was preceeded by 1BC, with no year zero.   */
  343.     i = Max(cch-1, 0);
  344.     if (i && sz[i] == '.')
  345.       i--;
  346.     if (i && ChCap(sz[i]) == 'C')
  347.       i--;
  348.     if (i && sz[i] == '.')
  349.       i--;
  350.     if (i && ChCap(sz[i]) == 'B')
  351.       n = 1 - n;
  352.   }
  353.   return n;
  354. }
  355.  
  356.  
  357. /* Given a string, return a floating point number corresponding to what the  */
  358. /* string indicates, based on a given parsing mode, like above for integers. */
  359.  
  360. real RParseSz(szEntry, pm)
  361. char *szEntry;
  362. int pm;
  363. {
  364.   char szLocal[cchSzMax], *sz, *pch, ch;
  365.   int cch, i, f = fFalse;
  366.   real r;
  367.  
  368.   /* First strip off any leading or trailing spaces. */
  369.   for (cch = 0; szLocal[cch] = szEntry[cch]; cch++)
  370.     ;
  371.   while (cch && szLocal[cch-1] <= ' ')
  372.     szLocal[--cch] = chNull;
  373.   for (sz = szLocal; *sz && *sz <= ' '; sz++, cch--);
  374.     ;
  375.   /* Capitalize all letters and make colons be periods to be like numbers. */
  376.   for (pch = sz; *pch; pch++) {
  377.     ch = *pch;
  378.     if (ch == ':')
  379.       ch = '.';
  380.     else
  381.       ch = ChCap(ch);
  382.     *pch = ch;
  383.   }
  384.   ch = sz[0];
  385.  
  386.   if (pm == pmTim) {
  387.     /* For times, process "Noon" and "Midnight" (or just "N" and "M"). */
  388.     if (ch == 'N')
  389.       return 12.0;
  390.     else if (ch == 'M')
  391.       return 0.0;
  392.   } else if (pm == pmDst) {
  393.     /* For the Daylight time flag, "Daylight", "Yes", and "True" (or just */
  394.     /* their first characters) are all indications to be ahead one hour.  */
  395.     if (ch == 'D' || ch == 'Y' || ch == 'T')
  396.       return 1.0;
  397.     /* "Standard", "No", and "False" mean the normal zero offset. */
  398.     else if (ch == 'S' || ch == 'N' || ch == 'F')
  399.       return 0.0;
  400.   } else if (pm == pmZon) {
  401.     /* For time zones, see if the abbrev is in a table, e.g. "EST" -> 5. */
  402.     for (i = 0; i < cZone; i++)
  403.       if (NCompareSz(sz, szZon[i]) == 0)
  404.         return rZon[i];
  405.   } else if (pm == pmLon || pm == pmLat) {
  406.     /* For locations, negate the value for an "E" or "S" in the middle    */
  407.     /* somewhere (e.g. "105E30" or "27:40S") for eastern/southern values. */
  408.     for (i = 0; i < cch; i++) {
  409.       ch = sz[i];
  410.       if (FCapCh(ch)) {
  411.         if (ch == 'E' || ch == 'S')
  412.           f = fTrue;
  413.         sz[i] = '.';
  414.         i = cch;
  415.       }
  416.     }
  417.     ch = sz[0];
  418.   }
  419.  
  420.   /* Anything still at this point should be in a numeric format. */
  421.   if (!FNumCh(ch) && ch != '+' && ch != '-' && ch != '.')
  422.     return rLarge;
  423.   r = (f ? -1.0 : 1.0) * atof(sz);
  424.  
  425.   if (pm == pmTim) {
  426.     /* Backtrack over any time suffix, i.e. "AM", "p.m." and variations. */
  427.     i = Max(cch-1, 0);
  428.     if (i && sz[i] == '.')
  429.       i--;
  430.     if (i && sz[i] == 'M')
  431.       i--;
  432.     if (i && sz[i] == '.')
  433.       i--;
  434.     if (i) {
  435.       ch = sz[i];
  436.       if (ch == 'A')                   /* Adjust value appropriately */
  437.         r = r >= 12.0 ? r-12.0 : r;    /* if AM or PM suffix.        */
  438.       else if (ch == 'P')
  439.         r = r >= 12.0 ? r : r+12.0;
  440.     }
  441.   }
  442.   return r;
  443. }
  444.  
  445.  
  446. /* Stop and wait for the user to enter a line of text given a prompt to */
  447. /* display and a string buffer to fill with it.                         */
  448.  
  449. void InputString(szPrompt, sz)
  450. char *szPrompt, *sz;
  451. {
  452.   FILE *file;
  453.  
  454.   file = S; S = stdout;
  455.   PrintSz(szPrompt);
  456.   AnsiColor(kYellow);
  457.   PrintSz(" > ");
  458.   AnsiColor(kDefault);
  459.   if (gets(sz) == NULL)    /* Pressing control-D will terminate the */
  460.     Terminate(tcForce);    /* program (at least on some machines.)  */
  461.   S = file;
  462.   is.cchCol = 0;
  463. }
  464.  
  465.  
  466. /* Prompt the user for a floating point value, parsing as appropriate, and */
  467. /* make sure it conforms to the specified bounds before returning it.      */
  468.  
  469. int NInputRange(szPrompt, low, high, pm)
  470. char *szPrompt;
  471. int low, high;
  472. int pm;
  473. {
  474.   char szLine[cchSzDef];
  475.   int n;
  476.  
  477.   loop {
  478.     InputString(szPrompt, szLine);
  479.     n = NParseSz(szLine, pm);
  480.     if (FBetween(n, low, high))
  481.       return n;
  482.     sprintf(szLine, "Value %d out of range from %d to %d.", n, low, high);
  483.     PrintWarning(szLine);
  484.   }
  485. }
  486.  
  487.  
  488. /* This is identical to above except it takes/returns floating point values. */
  489.  
  490. real RInputRange(szPrompt, low, high, pm)
  491. char *szPrompt;
  492. real low, high;
  493. int pm;
  494. {
  495.   char szLine[cchSzDef];
  496.   real r;
  497.  
  498.   loop {
  499.     InputString(szPrompt, szLine);
  500.     r = RParseSz(szLine, pm);
  501.     if (FBetween(r, low, high))
  502.       return r;
  503.     sprintf(szLine, "Value %.0f out of range from %.0f to %.0f.",
  504.       r, low, high);
  505.     PrintWarning(szLine);
  506.   }
  507. }
  508.  
  509.  
  510. /* This important procedure gets all the parameters defining the chart that  */
  511. /* will be worked with later. Given a "filename", it gets from it all the    */
  512. /* pertinent chart information. This is more than just reading from a file - */
  513. /* the procedure also takes care of the cases of prompting the user for the  */
  514. /* information and using the time functions to determine the date now - the  */
  515. /* program considers these cases "virtual" files. Furthermore, when reading  */
  516. /* from a real file, we have to check if it was written in the -o0 format.   */
  517.  
  518. bool FInputData(szFile)
  519. char *szFile;
  520. {
  521.   FILE *file;
  522.   char sz[cchSzDef], ch;
  523.   int i, fT;
  524.   real k, l, m;
  525.  
  526.   /* If we are to read from the virtual file "nul" that means to leave the */
  527.   /* chart information alone with whatever settings it may have already.   */
  528.  
  529.   if (NCompareSz(szFile, szNulCore) == 0) {
  530.     is.fHaveInfo = fTrue;
  531.     return fTrue;
  532.   }
  533.  
  534.   /* If we are to read from the virtual file "set" then that means use a   */
  535.   /* particular set of chart information generated earlier in the program. */
  536.  
  537.   if (NCompareSz(szFile, szSetCore) == 0) {
  538.     is.fHaveInfo = fTrue;
  539.     ciCore = ciSave;
  540.     return fTrue;
  541.   }
  542.  
  543. #ifdef TIME
  544.   /* If we are to read from the file "now" then that means use the time */
  545.   /* functions to calculate the present date and time.                  */
  546.  
  547.   if (NCompareSz(szFile, szNowCore) == 0) {
  548.     is.fHaveInfo = fTrue;
  549.     SS = us.dstDef; ZZ = us.zonDef; OO = us.lonDef; AA = us.latDef;
  550.     GetTimeNow(&MM, &DD, &YY, &TT, ZZ-SS);
  551.     return fTrue;
  552.   }
  553. #endif
  554.  
  555.   /* If we are to read from the file "tty" then that means prompt the user */
  556.   /* for all the chart information.                                        */
  557.  
  558.   if (NCompareSz(szFile, szTtyCore) == 0) {
  559.     file = S; S = stdout;
  560.     if (!us.fNoSwitches) {
  561.       /* Temporarily disable an internal redirection of output to a file  */
  562.       /* because we always want user headers and prompts to be displayed. */
  563.  
  564.       AnsiColor(kWhite);
  565.       sprintf(sz, "** %s version %s ", szAppName, szVersionCore); PrintSz(sz);
  566.       sprintf(sz, "(See '%cHc' switch for copyrights and credits.) **\n",
  567.         chSwitch); PrintSz(sz);
  568.       AnsiColor(kDefault);
  569.       sprintf(sz, "   Invoke as '%s %cH' for list of command line options.\n",
  570.         ProcessProgname(is.szProgName), chSwitch); PrintSz(sz);
  571.     }
  572.  
  573.     MM = NInputRange("Enter month for chart (e.g. '8' 'Aug')",
  574.       1, 12, pmMon);
  575.     DD = NInputRange("Enter day   for chart (e.g. '1' '31') ",
  576.       1, DayInMonth(MM, 0), pmDay);
  577.     YY = NInputRange("Enter year  for chart (e.g. '1995')   ",
  578.       -5000, 5000, pmYea);
  579.     if (FBetween(YY, 0, 99)) {
  580.       sprintf(sz,
  581.         "Assuming first century A.D. is really meant instead of %d.",
  582.         1900 + YY);
  583.       PrintWarning(sz);
  584.     }
  585.     TT = RInputRange("Enter time  for chart (e.g. '18:30' '6:30pm')  ",
  586.       -2.0, 24.0, pmTim);
  587.     SS = us.fWriteOld ? 0.0 :
  588.       RInputRange("Enter if Daylight time in effect (e.g. 'y' '1')",
  589.       -24.0, 24.0, pmDst);
  590.     ZZ = RInputRange("Enter time zone (e.g. '5' 'ET' for Eastern)    ",
  591.       -24.0, 24.0, pmZon);
  592.     if ((int)(RFract(ZZ) * 100.0 + rRound) == 50) {
  593.       PrintWarning(
  594.         "Assuming unusual zone of 50 minutes after the hour instead of 30.");
  595.     }
  596.     OO = RInputRange("Enter Longitude of place (e.g. '122W20')",
  597.       -rDegHalf, rDegHalf, pmLon);
  598.     AA = RInputRange("Enter Latitude  of place (e.g. '47N36') ",
  599.       -rDegQuad, rDegQuad, pmLat);
  600.     if (!us.fWriteOld) {
  601.       InputString("Enter name or title for chart ", sz);
  602.       ciCore.nam = SzPersist(sz);
  603.       InputString("Enter name of city or location", sz);
  604.       ciCore.loc = SzPersist(sz);
  605.     }
  606.     PrintL();
  607.     is.cchRow = 0;
  608.     S = file;
  609.     return fTrue;
  610.   }
  611.  
  612.   /* Now that the special cases are taken care of, we can assume we are */
  613.   /* to read from a real file.                                          */
  614.  
  615.   file = FileOpen(szFile, 1);
  616.   if (file == NULL)
  617.     return fFalse;
  618.   is.fHaveInfo = fTrue;
  619.   ch = getc(file); ungetc(ch, file);
  620.  
  621.   /* Read the chart parameters from a standard command switch file. */
  622.  
  623.   if (ch == '@') {
  624.     fT = is.fSzPersist; is.fSzPersist = fFalse;
  625.     if (!FProcessSwitchFile(szFile, file))
  626.       return fFalse;
  627.     is.fSzPersist = fT;
  628.  
  629.   /* Read the chart info from an older style -o list of seven numbers. */
  630.  
  631.   } else if (FNumCh(ch)) {
  632.     SS = 0.0;
  633.     fscanf(file, "%d%d%d", &MM, &DD, &YY);
  634.     fscanf(file, "%lf%lf%lf%lf", &TT, &ZZ, &OO, &AA);
  635.     if (!FValidMon(MM) || !FValidDay(DD, MM, YY) || !FValidYea(YY) ||
  636.       !FValidTim(TT) || !FValidZon(ZZ) || !FValidLon(OO) || !FValidLat(AA)) {
  637.       PrintWarning("Values in old style chart info file are out of range.");
  638.       return fFalse;
  639.     }
  640.  
  641.   /* Read the actual chart positions from a file produced with the -o0. */
  642.  
  643.   } else if (ch == 'S') {
  644.     MM = -1;
  645.  
  646.     /* Hack: A negative month value means the chart parameters are invalid, */
  647.     /* hence -o0 is in effect and we can assume the chart positions are     */
  648.     /* already in memory so we don't have to calculate them later.          */
  649.  
  650.     for (i = 1; i <= oNorm; i++) {
  651.       fscanf(file, "%s%lf%lf%lf", sz, &k, &l, &m);
  652.       planet[i] = Mod((l-1.0)*30.0+k+m/60.0);
  653.       fscanf(file, "%s%lf%lf", sz, &k, &l);
  654.       if ((m = k+l/60.0) > rDegHalf)
  655.         m = rDegMax - m;
  656.       planetalt[i] = m;
  657.       ret[i] = RFromD(sz[1] == 'D' ? 1.0 : -1.0);
  658.  
  659.       /* -o0 files from versions 3.05 and before don't have the uranians in  */
  660.       /* them. Be prepared to skip over them in old files for compatibility. */
  661.  
  662.       if (i == oVtx) {
  663.         while (getc(file) >= ' ')
  664.           ;
  665.         if ((ch = getc(file)) != 'H')
  666.           i = cuspHi;
  667.         else
  668.           i = cObj;
  669.       }
  670.       if (i == oNod)
  671.         i = oFor-1;
  672.       else if (i == oFor)
  673.         i = oLil-1;
  674.       else if (i == oLil)
  675.         i = oEP -1;
  676.       else if (i == oEP)
  677.         i = oVtx-1;
  678.     }
  679.     for (i = 1; i <= cSign/2; i++) {
  680.       fscanf(file, "%s%lf%lf%lf", sz, &k, &l, &m);
  681.       house[i+6] = Mod((house[i] = Mod((l-1.0)*30.0+k+m/60.0))+rDegHalf);
  682.     }
  683.     for (i = 1; i <= cSign; i++)
  684.       planet[cuspLo-1+i] = house[i];
  685.     planet[oMC] = planet[oLil]; planet[oNad] = Mod(planet[oMC]  + rDegHalf);
  686.     planet[oAsc] = planet[oEP]; planet[oDes] = Mod(planet[oAsc] + rDegHalf);
  687.     planet[oSou] = Mod(planet[oNod] + rDegHalf); ret[oSou] = ret[oNod];
  688.  
  689.   } else {
  690.     PrintWarning("The chart info file is not in any valid format.");
  691.     return fFalse;
  692.   }
  693.   fclose(file);
  694.   return fTrue;
  695. }
  696.  
  697. /* io.c */
  698.